﻿#include "dXml.h"
#include <QDir>
#include "XmlDataDoc.h"
#include <QtXml/QtXml>
#include "MathManage.h"
#include "MathExpressResolve.h"
#include "MathExpressBody.h"
#include "MathPairUnit.h"

QDomElement dXml::toDocElement(QDomDocument& doc
	, XmlDataUnit* node) {
	QString tagName = node->nodeName( );
	QDomElement thisElement = doc.createElement(tagName.isEmpty( ) ? "defalut" : tagName);
	QList<XmlDataUnit*>* xmlDataUnits = node->getChildrens( );
	int len = xmlDataUnits->length( );
	if( len ) {
		for( int i = 0;i < len;++i ) {
			QDomElement element = toDocElement(doc, xmlDataUnits->at(i));
			thisElement.appendChild(element);
		}
	} else {
		QDomText textNode = doc.createTextNode(node->getText( ));
		thisElement.appendChild(textNode);
	}
	QHash<QString, QString>* attrs = node->getAttrs( );
	// 设置属性
	QHash<QString, QString>::iterator iterator = attrs->begin( );
	QHash<QString, QString>::iterator end = attrs->end( );
	for( ;iterator != end;++iterator ) {
		thisElement.setAttribute(iterator.key( ), iterator.value( ));
	}
	/*QDomComment domComment = doc.createComment(node.getComment('\n'));
	thisElement.appendChild(domComment);*/

	return thisElement;
}

bool dXml::save(QList<XmlDataUnit*>& nodes
	, const QString& file
	, QDomDocument* qDomDocument
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	bool hasProcessingInstruction = false;
	if( qDomDocument != Q_NULLPTR ) {
		QDomNodeList list = qDomDocument->childNodes( );
		int length = list.length( );
		for( int i = 0;i < length;++i ) {
			QDomNode domNode = list.at(i);
			if( domNode.isProcessingInstruction( ) ) {
				hasProcessingInstruction = true;
				break;
			}
		}
	} else {
		qDomDocument = new QDomDocument( );
	}
	if( !hasProcessingInstruction ) {
		QDomProcessingInstruction processingInstruction = qDomDocument->createProcessingInstruction(dXml::getProcessingInstructionTarget( ), dXml::getProcessingInstructionData( ));
		qDomDocument->insertBefore(processingInstruction, qDomDocument->firstChild( ));
	}
	QList<XmlDataUnit*>::iterator iterator = nodes.begin( );
	QList<XmlDataUnit*>::iterator end = nodes.end( );
	for( ;iterator != end;++iterator )
		qDomDocument->appendChild(dXml::toDocElement(*qDomDocument, *iterator));
	QDir dir;
	QFileInfo info(file);
	dir.mkpath(info.absolutePath( ));
	QFile writefile(file);
	if( !writefile.open(QIODevice::WriteOnly | QIODevice::Unbuffered) )
		return false;
	QTextStream outStream(&writefile);
	qDomDocument->save(outStream, tabSize, edcoding);
	delete qDomDocument;
	writefile.close( );
	return true;
}

dXml::XmlDataDoc dXml::toMaths(const char* szExpression) {
	QStack<int> left; // 左括号
	QQueue<int> right; // 右括号
	MathExpress::MathManage manage(szExpression);

	// QVector<MathExpress> map; // 分析映射
	// 查找下标
	int index = 0;
	// 括号配对
	int make = 0;
	// 行 make
	int leftMake = 0;
	for( ;szExpression[index];++index ) {
		for( ;;++index ) {
			auto& ch = szExpression[index];
			if( ch == '\0' || ch == ')' )
				break;
			if( ch == '(' ) {
				left.push(index);
				++make;
			}
		}
		for( ;;++index ) {
			auto& ch = szExpression[index];
			if( ch == '\0' || !make )
				break;
			if( ch == ')' ) {
				right.enqueue(index);
				--make;
			}
		}
		// 发生异常
		if( make )
			return XmlDataDoc( );
		// 配对完成

		for( int rightMake = 0;rightMake < left.size( );++rightMake ) {
			MathExpress::MathExpressBody* body = new MathExpress::MathExpressBody;
			MathExpress::MathExpressResolve* pResolve = new MathExpress::MathExpressResolve(body);
			MathExpress::MathPairUnit* pair = new MathExpress::MathPairUnit(body, pResolve);
			//		map.append(MathExpress(left.pop( ), right.dequeue( ), leftMake, rightMake));
			manage.append(pair, true);
		}
		++leftMake;
	}
	return XmlDataDoc( );
}

dXml::XmlDataDoc dXml::toMaths(const QString& expression) {
	QRegExp regExp(R"(\d+)");
	QStringList result;
	int posStart = 0;
	int posEnd = 0;
	int length = expression.length( );
	posStart = regExp.indexIn(expression, posStart);
	posEnd = regExp.matchedLength( );
	QStringRef midRef = expression.midRef(posStart, posEnd);
	QString chars = midRef.toString( );
	return XmlDataDoc( );
}

void dXml::domDocumentToDxmlUnitDoc(dXml::XmlDataUnit* saveToData
	, QString& outPITarget
	, QString& outPIData
	, QDomDocument doc) {
	QDomNodeList domNodeList = doc.childNodes( );
	int listSize = domNodeList.length( );
	for( int index = 0;index < listSize;++index ) {
		QDomNode domNode = domNodeList.at(index);
		dXml::XmlDataUnit* xmlDataUnit = Q_NULLPTR;
		if( domNode.isElement( ) ) {
			// 获取对象
			QDomElement element = domNode.toElement( );
			saveToData->setName(element.tagName( ));
			// 获取属性
			QDomNamedNodeMap attriMap = element.attributes( );
			int length = attriMap.length( );
			for( int attrIndex = 0;attrIndex < length;++attrIndex ) {
				QDomNode item = attriMap.item(attrIndex);
				if( item.isAttr( ) ) {
					QDomAttr attr = item.toAttr( );
					QString attrName = attr.name( );
					QString attrValte = attr.value( );
					saveToData->appendAttr(attrName, attrValte);
				}
			}
		}
		if( domNode.isProcessingInstruction( ) ) {
			QDomProcessingInstruction instruction = domNode.toProcessingInstruction( );
			outPITarget = instruction.target( );
			outPIData = instruction.data( );
		}
		if( domNode.isText( ) ) {
			QDomText text = domNode.toText( );
			saveToData->setText(text.data( ));
		}
		QList<dXml::XmlDataUnit*> dataUnits = docChildRootNodesToXmlDataUnity(domNode.childNodes( ), saveToData);
		QList<dXml::XmlDataUnit*>::iterator iterator = dataUnits.begin( );
		QList<dXml::XmlDataUnit*>::iterator end = dataUnits.end( );
		for( ;iterator != end;++iterator ) {
			saveToData->appendChildren(*iterator);
		}
	}
}

bool dXml::readStringToXml(XmlDataUnit* saveToData
	, QString& outPITarget
	, QString& outPIData
	, const QString& strStream) {
	QDomDocument doc;
	if( ! doc.setContent(strStream) ) {
		return false;
	}
	domDocumentToDxmlUnitDoc(saveToData, outPITarget, outPIData, doc);
	return true;
}

bool dXml::readXmlFile(XmlDataUnit* saveToData
	, QString& outPITarget
	, QString& outPIData
	, const QString& file) {
	QFile readFile(file);
	if( !readFile.open(QIODevice::ReadOnly | QIODevice::Text) )
		return false;
	QDomDocument doc;
	QByteArray bytes = readFile.readAll( );
	QTextCodec* codec = QTextCodec::codecForName("utf-8");
	auto&& fileToText = codec->toUnicode(bytes);
	readFile.close( );
	doc.setContent(fileToText);
	QString string = doc.toString( );
	domDocumentToDxmlUnitDoc(saveToData, outPITarget, outPIData, doc);
	return true;
}

bool dXml::readXmlFile(XmlDataUnit* saveToData
	, const QString& file) {
	QString outPITarget;
	QString outPIData;
	return readXmlFile(saveToData, outPITarget, outPIData, file);
}

QList<dXml::XmlDataUnit*> dXml::docChildRootNodesToXmlDataUnity(const QDomNodeList& domNodes
	, XmlDataUnit* paren) {
	QList<XmlDataUnit*> result;
	if( !domNodes.isEmpty( ) ) {
		int size = domNodes.size( );
		for( int index = 0;index < size;++index ) {
			XmlDataUnit* xmlDataUnit = Q_NULLPTR;
			QDomNode domNode = domNodes.at(index);
			if( domNode.isElement( ) ) {
				// 获取对象
				QDomElement element = domNode.toElement( );
				xmlDataUnit = new XmlDataUnit(element.tagName( ));
				result.append(xmlDataUnit);
				// 获取属性
				QDomNamedNodeMap attriMap = element.attributes( );
				int length = attriMap.length( );
				for( int attrIndex = 0;attrIndex < length;++attrIndex ) {
					QDomNode item = attriMap.item(attrIndex);
					if( item.isAttr( ) ) {
						QDomAttr attr = item.toAttr( );
						QString attrName = attr.name( );
						QString attrValte = attr.value( );
						xmlDataUnit->appendAttr(attrName, attrValte);
					}
				}
			}
			if( domNode.isText( ) ) {
				QDomText text = domNode.toText( );
				paren->setText(text.data( ));
			}
			QList<dXml::XmlDataUnit*> dataUnits = docChildRootNodesToXmlDataUnity(domNode.childNodes( ), xmlDataUnit);
			QList<dXml::XmlDataUnit*>::iterator iterator = dataUnits.begin( );
			QList<XmlDataUnit*>::iterator end = dataUnits.end( );
			for( ;iterator != end;++iterator ) {
				xmlDataUnit->appendChildren(*iterator);
			}
		}
	}
	return result;
}

dXml::XmlDataDoc dXml::xmlBlend(const dXml::XmlDataDoc& leftDoc
	, const dXml::XmlDataDoc& rightDoc
	, int dupStatus) {
	XmlDataDoc result;
	QList<XmlDataUnit*>* leftChildrens = leftDoc.getChildrens( );
	QList<XmlDataUnit*>* rightChildrens = rightDoc.getChildrens( );
	QVector<XmlDataUnit*> childrens = xmlBlendUnitList(leftChildrens, rightChildrens, dupStatus);
	QList<XmlDataUnit*> list = childrens.toList( );
	result.appendChildrens(list);
	return result;
}

int dXml::xmlBlendUnitSortOrder(const XmlDataUnit* left
	, const XmlDataUnit* right) {
	auto name = left->nodeName( );
	auto nodeName = right->nodeName( );
	int compare = name.compare(nodeName);
	return compare > 0;
}

int dXml::xmlBlendUnitSortReOrder(const XmlDataUnit* left
	, const XmlDataUnit* right) {
	auto name = left->nodeName( );
	auto nodeName = right->nodeName( );
	int compare = name.compare(nodeName);
	return compare < 0;
}

QVector<dXml::XmlDataUnit*> dXml::xmlBlendUnitList(QList<XmlDataUnit*>* leftDoc
	, QList<XmlDataUnit*>* rightDoc
	, int dupStatus) {
	QVector<dXml::XmlDataUnit*> buffList;
	QVector<dXml::XmlDataUnit*> result;
	if( dupStatus == 1 ) {
		if( !leftDoc->isEmpty( ) ) {
			for( int i = 0;i < leftDoc->size( );++i )
				buffList.append(leftDoc->at(i));
		}
		if( !rightDoc->isEmpty( ) ) {
			for( int i = 0;i < rightDoc->size( );++i )
				buffList.append(rightDoc->at(i));
		}
		for( auto& unit : buffList ) {
			result.append(new XmlDataUnit(*unit));
		}
	} else if( dupStatus == 2 ) {
		for( int i = 0;i < leftDoc->size( );++i )
			buffList.append(leftDoc->at(i));
		for( int i = 0;i < rightDoc->size( );++i ) {
			XmlDataUnit* const unit = rightDoc->at(i);
			bool have = false;
			for( int iJ = 0;iJ < leftDoc->size( );++iJ ) {
				if( leftDoc->at(i)->nodeName( ) == unit->nodeName( ) )
					have = true;
			}
			if( !have )
				buffList.append(rightDoc->at(i));
		}
		for( auto& unit : buffList ) {
			result.append(new XmlDataUnit(*unit));
		}
	}
	qSort(result.begin( ), result.end( ), dXml::xmlBlendUnitSortOrder);
	return result;
}

/*inline const char* dXml::getProcessingInstructionTarget( ) {
	static const char* instance = "xml";
	return instance;
}

inline const char* dXml::getProcessingInstructionData( ) {
	static const char* instance = "version=\"1.0\" encoding=\"UTF-8\"";
	return instance;
}*/

/*
 *
 *const char* getProcessingInstructionTarget( ); {
		
	}
 * 
 */
/*
 *
 * {
		
	}
 */
